package timing.ukulele.storage.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import timing.ukulele.storage.dictionary.RoleTypeEnum;
import timing.ukulele.storage.handler.LoginFailureHandler;
import timing.ukulele.storage.handler.LoginSuccessHandler;
import timing.ukulele.storage.handler.MyLogoutSuccessHandler;
import timing.ukulele.storage.jwt.JWTFilter;
import timing.ukulele.storage.jwt.JWTProvider;

import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;

@Configuration
public class SecurityConfiguration {

    private final UserDetailsService userDetailsService;
    private final JWTProvider jwtProvider;
    private final LoginSuccessHandler loginSuccessHandler;
    private final LoginFailureHandler loginFailureHandler;
    private final MyLogoutSuccessHandler myLogoutSuccessHandler;

    public SecurityConfiguration(
            MyLogoutSuccessHandler myLogoutSuccessHandler,
            LoginFailureHandler loginFailureHandler,
            LoginSuccessHandler loginSuccessHandler,
            JWTProvider jwtProvider,
            UserDetailsService userDetailsService) {
        this.myLogoutSuccessHandler = myLogoutSuccessHandler;
        this.loginFailureHandler = loginFailureHandler;
        this.loginSuccessHandler = loginSuccessHandler;
        this.jwtProvider = jwtProvider;
        this.userDetailsService = userDetailsService;
    }

    @Bean
    public AuthenticationProvider authenticationProvider(PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider(passwordEncoder);
        provider.setUserDetailsService(userDetailsService);
        return provider;
    }

    @Bean
    public SecurityFilterChain configure(HttpSecurity http) throws Exception {


        http.httpBasic(HttpBasicConfigurer::disable)
                .authorizeHttpRequests(
                        request ->
                                request
                                        .requestMatchers("/v3/api-docs/**", "/v3/api-docs.yaml", "/swagger-ui/**", "/swagger-ui.html").permitAll()
                                        .requestMatchers("/group/**", "/user/**").hasAuthority(RoleTypeEnum.ADMIN.name()) // 需要角色
                                        .anyRequest().authenticated()// 所有的请求都需要登录

                ) // 配置请求权限
                // 配置登录url，和登录成功处理器
                .formLogin(
                        login ->
                                login.loginProcessingUrl("/login").successHandler(loginSuccessHandler).failureHandler(loginFailureHandler)

                )
                // 取消csrf防护
                .csrf(AbstractHttpConfigurer::disable)
                // 配置登出url，和登出成功处理器
                .logout(logout ->
                {
                    logout.logoutUrl("/logout").logoutSuccessHandler(myLogoutSuccessHandler);
                })
                // 在UsernamePasswordAuthenticationFilter之前执行我们添加的JWTFilter
                .addFilterBefore(new JWTFilter(jwtProvider), UsernamePasswordAuthenticationFilter.class);

        http.sessionManagement(session -> session.sessionCreationPolicy(STATELESS)); //设置无状态

        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}